
// ===============================================================================================================
// -*- C++ -*-
//
// Log.cpp - Definitions for the Log and LogListener classes.
//
// Copyright (c) 2011 Guilherme R. Lampert
// guilherme.ronaldo.lampert@gmail.com
//
// This code is licenced under the MIT license.
//
// This software is provided "as is" without express or implied
// warranties. You may freely copy and compile this source into
// applications you distribute provided that the copyright text
// above is included in the resulting source code.
//
// ===============================================================================================================

#include <Log.hpp>
#include <cstdarg> // For va_list, va_start(), va_end(), etc.

// =========================================================
// Log Class Implementation
// =========================================================

Log& Log::Instance(void)
{
	static Log logInstance; // Create a new static log instance and return it.
	return (logInstance);
}

bool Log::Open(const std::string & fileName)
{
	Close(); // Make sure we are not Open already.

	if ((filePointer = fopen(fileName.c_str(), "wt")) != 0)
	{
		setvbuf(filePointer, 0, _IONBF, 0); // Unbuffered file IO.
		return (true);
	}

	return (false);
}

bool Log::IsOpen(void) const
{
	return (filePointer != 0);
}

void Log::Close(void)
{
	if (filePointer != 0)
	{
		fflush(filePointer);
		fclose(filePointer);
		filePointer = 0;
	}
}

void Log::Flush(void)
{
	if (filePointer != 0)
	{
		fflush(filePointer);
	}
}

void Log::AddListener(LogListener * listener)
{
	if (listener != 0)
	{
		Log::ListenersList::const_iterator it = listeners.begin();
		Log::ListenersList::const_iterator end = listeners.end();

		while (it != end)
		{
			if ((*it) == listener)
			{
				return; // This one is already in the list, so don't add it.
			}

			++it;
		}

		listeners.push_back(listener);
	}
}

void Log::RemoveListener(LogListener * listener)
{
	if (listener != 0)
	{
		Log::ListenersList::const_iterator it = listeners.begin();
		Log::ListenersList::const_iterator end = listeners.end();

		while (it != end)
		{
			if ((*it) == listener)
			{
				listeners.erase(it); // Object found, remove it.
				break;
			}

			++it;
		}
	}
}

size_t Log::NumberOfListeners(void) const
{
	return (listeners.size());
}

void Log::Write(const char * format, ...)
{
	char charBuf[MAX_STRING_LEN];
	va_list argList;

	va_start(argList, format);
	_vsnprintf(charBuf, sizeof(charBuf), format, argList);
	va_end(argList);

	if (filePointer != 0) // Can only print if the log is open
	{
		fputs(charBuf, filePointer);
	}
	else
	{
		// Probably first time used. Try to open:
		Open("Debug.log");

		if (filePointer != 0) // On success, write
		{
			fputs(charBuf, filePointer);
		}
	}

	// Notify all the listeners:
	NotifyListeners(charBuf);
}

void Log::NotifyListeners(const std::string & msg) const
{
	if (!listeners.empty())
	{
		Log::ListenersList::const_iterator it = listeners.begin();
		Log::ListenersList::const_iterator end = listeners.end();

		while (it != end)
		{
			(*it)->ReceiveMessage(msg);
			++it;
		}
	}
}

Log& Log::operator << (const std::string & text)
{
	Write(text.c_str());
	return (*this);
}

Log& Log::operator << (const char * text)
{
	Write(text);
	return (*this);
}

Log& Log::operator << (char ch)
{
	Write("%c", ch);
	return (*this);
}

Log& Log::operator << (const void * ptr)
{
	Write("%p", ptr);
	return (*this);
}

Log& Log::operator << (short value)
{
	Write("%i", value);
	return (*this);
}

Log& Log::operator << (unsigned short value)
{
	Write("%u", value);
	return (*this);
}

Log& Log::operator << (int value)
{
	Write("%i", value);
	return (*this);
}

Log& Log::operator << (unsigned int value)
{
	Write("%u", value);
	return (*this);
}

Log& Log::operator << (float value)
{
	Write("%f", value);
	return (*this);
}

Log& Log::operator << (double value)
{
	Write("%lf", value);
	return (*this);
}

Log::~Log(void)
{
	Close();
}